<script setup>
import { computed, nextTick, reactive, ref, watchEffect } from 'vue'
import { useThemeVars } from 'naive-ui'
import Save from '@/components/icons/Save.vue'
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
import { decodeRedisKey } from '@/utils/key_convert.js'
import useBrowserStore from 'stores/browser.js'
import FormatSelector from '@/components/content_value/FormatSelector.vue'
import IconButton from '@/components/common/IconButton.vue'
import FullScreen from '@/components/icons/FullScreen.vue'
import WindowClose from '@/components/icons/WindowClose.vue'
import Pin from '@/components/icons/Pin.vue'
import OffScreen from '@/components/icons/OffScreen.vue'
import ContentEditor from '@/components/content_value/ContentEditor.vue'
import { isEmpty, toString } from 'lodash'

const props = defineProps({
    keyPath: String,
    show: {
        type: Boolean,
    },
    field: {
        type: [String, Number],
    },
    value: {
        type: [String, Array],
    },
    fieldLabel: {
        type: String,
    },
    valueLabel: {
        type: String,
    },
    decode: {
        type: String,
    },
    format: {
        type: String,
    },
    fieldReadonly: {
        type: Boolean,
    },
    fullscreen: {
        type: Boolean,
    },
})

const themeVars = useThemeVars()
const browserStore = useBrowserStore()
const emit = defineEmits([
    'update:field',
    'update:value',
    'update:decode',
    'update:format',
    'update:fullscreen',
    'save',
    'close',
])

watchEffect(
    () => {
        if (props.show && !isEmpty(props.keyPath)) {
            onFormatChanged(props.decode, props.format)
        } else {
            viewAs.value = ''
        }
    },
    {
        flush: 'post',
    },
)

const loading = ref(false)
const isPin = ref(false)
const viewAs = reactive({
    field: '',
    value: '',
    format: formatTypes.RAW,
    decode: decodeTypes.NONE,
})
const displayValue = computed(() => {
    if (loading.value) {
        return ''
    }
    if (viewAs.value == null) {
        return decodeRedisKey(props.value)
    }
    return viewAs.value
})
const editingContent = ref('')
const enableSave = computed(() => {
    return toString(props.field) !== viewAs.field || editingContent.value !== viewAs.value
})

const viewLanguage = computed(() => {
    switch (viewAs.format) {
        case formatTypes.JSON:
        case formatTypes.UNICODE_JSON:
            return 'json'
        case formatTypes.YAML:
            return 'yaml'
        case formatTypes.XML:
            return 'xml'
        default:
            return 'plaintext'
    }
})

/**
 *
 * @param {decodeTypes|null} decode
 * @param {formatTypes|null} format
 * @return {Promise<void>}
 */
const onFormatChanged = async (decode = null, format = null) => {
    try {
        loading.value = true
        const {
            value,
            decode: retDecode,
            format: retFormat,
        } = await browserStore.convertValue({
            value: props.value,
            decode,
            format,
        })
        viewAs.field = props.field + ''
        editingContent.value = viewAs.value = value
        viewAs.decode = decode || retDecode
        viewAs.format = format || retFormat
        emit('update:decode', viewAs.decode)
        emit('update:format', viewAs.format)
    } finally {
        loading.value = false
    }
}

const onInput = (content) => {
    editingContent.value = content
}

const onToggleFullscreen = () => {
    emit('update:fullscreen', !!!props.fullscreen)
}

const onClose = () => {
    isPin.value = false
    emit('close')
}

const onSave = () => {
    emit('save', viewAs.field, editingContent.value, viewAs.decode, viewAs.format)
    if (!isPin.value) {
        nextTick().then(onClose)
    }
}
</script>

<template>
    <div v-show="show" class="entry-editor flex-box-v">
        <n-card :title="$t('interface.edit_row')" autofocus class="flex-item-expand" size="small">
            <div class="editor-content flex-box-v flex-item-expand">
                <!-- field -->
                <div class="editor-content-item flex-box-v">
                    <div class="editor-content-item-label">{{ props.fieldLabel }}</div>
                    <n-input
                        v-model:value="viewAs.field"
                        :placeholder="props.field + ''"
                        :readonly="props.fieldReadonly"
                        class="editor-content-item-input"
                        type="text" />
                </div>

                <!-- value -->
                <div class="editor-content-item flex-box-v flex-item-expand">
                    <div class="editor-content-item-label">{{ props.valueLabel }}</div>
                    <content-editor
                        :border="true"
                        :content="displayValue"
                        :key-path="viewAs.field"
                        :language="viewLanguage"
                        class="flex-item-expand"
                        @input="onInput"
                        @reset="onInput"
                        @save="onSave" />
                    <format-selector
                        :decode="viewAs.decode"
                        :format="viewAs.format"
                        style="margin-top: 5px"
                        @format-changed="(d, f) => onFormatChanged(d, f)" />
                </div>
            </div>
            <template #header-extra>
                <n-space :size="5">
                    <icon-button
                        :button-class="{ 'pinable-btn': true, 'unpin-btn': !isPin, 'pin-btn': isPin }"
                        :icon="Pin"
                        :size="19"
                        :t-tooltip="isPin ? 'interface.unpin_edit' : 'interface.pin_edit'"
                        stroke-width="4"
                        @click="isPin = !isPin" />
                    <icon-button
                        :button-class="['pinable-btn', 'unpin-btn']"
                        :icon="props.fullscreen ? OffScreen : FullScreen"
                        :size="18"
                        stroke-width="5"
                        t-tooltip="interface.fullscreen"
                        @click="onToggleFullscreen" />
                    <icon-button
                        :button-class="['pinable-btn', 'unpin-btn']"
                        :icon="WindowClose"
                        :size="18"
                        stroke-width="5"
                        t-tooltip="menu.close"
                        @click="onClose" />
                </n-space>
            </template>
            <template #action>
                <n-space :wrap="false" :wrap-item="false" justify="end">
                    <n-button :disabled="!enableSave" :secondary="enableSave" type="primary" @click="onSave">
                        <template #icon>
                            <n-icon :component="Save" />
                        </template>
                        {{ $t('common.update') }}
                    </n-button>
                </n-space>
            </template>
        </n-card>
    </div>
</template>

<style lang="scss" scoped>
.entry-editor {
    padding-left: 2px;
    box-sizing: border-box;
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    z-index: 100;

    .editor-content {
        &-item {
            &:not(:last-child) {
                margin-bottom: 16px;
            }

            &-label {
                height: 18px;
                color: v-bind('themeVars.textColor3');
                font-size: 13px;
                padding: 5px 0;
            }

            &-input {
            }
        }
    }
}

:deep(.n-card__content) {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
}

:deep(.n-card__action) {
    padding: 5px 10px;
    background-color: unset;
}

:deep(.pinable-btn) {
    padding: 3px;
    border-style: solid;
    border-width: 1px;
    border-radius: 3px;
}

:deep(.unpin-btn) {
    border-color: #0000;
}

:deep(.pin-btn) {
    border-color: v-bind('themeVars.iconColorDisabled');
    background-color: v-bind('themeVars.iconColorDisabled');
}

//:deep(.n-card--bordered) {
//    border-radius: 0;
//}
</style>
